#ifndef __LOG_H__
#define __LOG_H__

#include <memory>
#include <fstream>
#include <filesystem>
#include <string>
#include <mutex>
#include <future>
#include <thread>
#include <iostream>

#include <queue>

/*
* 异步日志
* 1.在外部使用时, IO 操作不影响主线程
* 2.根据初始化的日志文件路径进行输出
* 3.需要拥有输出时间、日志等级(log、warning、error...)、格式化字符串等功能
* 4.全局唯一
*/
namespace NetWork
{
    enum class LogLevel {
        Log, 
        Warning,
        Error
    };

    class Log {
    private:
        std::queue<std::string> Logs;

        std::string LogFileOutPutPath;
        std::ofstream OutPutStream;
        std::mutex LogMutex;

        //https://blog.csdn.net/qq_36239569/article/details/104520802
        //async 返回的 future 在析构时会阻塞至异步计算完成
        //Log 类中需要使用类成员获取, 避免阻塞
        std::future<void> FutureToLogFile;

        bool EndOfLife;

    private:
        Log();
        ~Log();

    private:
        void AsyncToLogFile();

    public:
        void SetLogFileOutPutPath(const std::string& path);

        template<typename... Args>
        void Write(LogLevel level, const std::string& format, const Args&... args) {
            std::string logLevel;
            switch (level)
            {
            case LogLevel::Log:
                logLevel = "[Log]    : ";
                break;
            case LogLevel::Warning:
                logLevel = "[Warning]: ";
                break;
            case LogLevel::Error:
                logLevel = "[Error]  : ";
                break;
            default:
                break;
            }

            std::string logFormat = logLevel + format;

            int formatSize = std::snprintf(nullptr, 0, logFormat.c_str(), args...);
            
            formatSize++;
            std::unique_ptr<char[]> str = std::make_unique<char[]>(formatSize);
            std::snprintf(str.get(), formatSize, logFormat.c_str(), args...);

            {
                std::lock_guard<std::mutex> lock(LogMutex);
                Logs.push(str.get());
            }
        }

    public:
        static Log& Instance();

        template<typename... Args>
        static void WriteLog(LogLevel level, const std::string& format, const Args&... args) {
            auto& log = Log::Instance();
            log.Write(level, format, args...);
        }
    };  
} // namespace NetWork



#endif